package com.sai.jsai.common.starter.mybatisplus.component;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author jisai
 * @version 0.0.1
 * @Description MyBatis Plus 构建 QueryWrapper
 * @createTime 2024/10/31
 */
@Slf4j
public class QueryGenerator {

    private static final List<String> USELESS_FIELDS = List.of("class", "pageNo", "pageSize");

    public static <T> QueryWrapper<T> initQueryWrapper(Object searchObj) {
        long start = System.currentTimeMillis();
        QueryWrapper<T> queryWrapper = new QueryWrapper<T>();
        assembledQueryWrapper(queryWrapper, searchObj);
        log.debug("initQueryWrapper cost: {}", System.currentTimeMillis() - start);
        return queryWrapper;
    }

    /**
     *
     * @param queryWrapper
     * @param searchObj
     */
    private static void assembledQueryWrapper(QueryWrapper<?> queryWrapper, Object searchObj) {
        // 利用反射机制获取类中的属性
        List<Field> fields = getClassFields(searchObj.getClass());

        for (Field field : fields) {
            try {
                field.setAccessible(true);
                String name = field.getName();

                // 如果字段在无用字段中，跳过该字段
                if (judgedIsUselessField(name) || isUnReadable(searchObj, name)) {
                    continue;
                }
                Object value = field.get(searchObj);
                if (value != null) {
                    queryWrapper.eq(name, value);
                }
            } catch (Exception e){
                log.warn("assembledQueryWrapper error", e);
            }
        }
    }

    /**
     * 判断是不是没用的属性
     *
     * @param name 被判断的属性名
     * @return 是无用属性返回true，否则返回false
     */
    private static boolean judgedIsUselessField(String name) {
        return USELESS_FIELDS.contains(name);
    }

    /**
     * 获取class的 包括父类的
     *
     * @param clazz 类
     * @return 类的所有属性
     */
    private static List<Field> getClassFields(Class<?> clazz) {
        List<Field> list = new ArrayList<>();
        Field[] fields;
        do {
            fields = clazz.getDeclaredFields();
            list.addAll(Arrays.asList(fields));
            clazz = clazz.getSuperclass();
        } while (clazz != Object.class && clazz != null);
        return list;
    }

    /**
     * 字段是否不可读
     *
     * @param searchObj 对象
     * @param fieldName 字段名
     * @return 是否不可读
     */
    private static boolean isUnReadable(Object searchObj, String fieldName) {
        PropertyDescriptor propertyDescriptor = BeanUtil.getPropertyDescriptor(searchObj.getClass(), fieldName);
        if (propertyDescriptor == null) {
            return true;
        }
        return propertyDescriptor.getReadMethod() == null;
    }

}
